
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <stdint.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#include <time.h>

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <pthread.h>

#define CACHE_SIZE			512

#define PACKAGE_TOTAL		1024
#define PACKAGE_INFO		(4 * 3)
#define PACKAGE_DATA		PACKAGE_TOTAL - PACKAGE_INFO

#define FILE_TOTAL			PACKAGE_DATA
#define FILE_INFO			(4 * 4)
#define FILE_DATA			FILE_TOTAL - FILE_INFO

struct file_block
{
	uint32_t block_type;	// 0: 文件名（客户端发）	1: 有效文件块（客户端发）	2: 块请求（服务端发）		3:	控制命令(具体命令从data提取，4个字节)
	uint32_t block_id;
	uint32_t block_total;
	uint32_t valid_len;
	uint8_t data[FILE_DATA];		// 控制命令：	0：成功		1：成功，请返回文件校验码	2：失败
};

struct net_package
{
	uint32_t type;			// 0: 文件上传服务			1: 文件下载服务
	uint32_t valid_len;
	uint32_t checksum;
	uint8_t data[PACKAGE_DATA];
};

struct socket_info
{
	uint32_t need_block_id;
	uint32_t need_block_total;
	char name[256];
	FILE *pf;
};

struct socket_info sinfo[CACHE_SIZE];

static uint32_t calc_checksum(void *data, uint32_t size)
{
	uint32_t i = 0, sum = 0xFFFFFFFF;
	for(i = 0; size > i; i += 4)
	{
		sum += *(unsigned char *)(data + i);
	}
	return sum;
}

static int file_block_send_command(int socket, uint32_t cmd, uint32_t data)
{
	struct file_block *block = NULL;
	struct net_package package;
	
	memset(&package, 0, PACKAGE_TOTAL);
	
	block = (struct file_block *)package.data;

	block->block_type = cmd;
	block->valid_len = 4;
	*(uint32_t *)block->data = data;

	package.type = 0;
	package.valid_len = FILE_INFO + block->valid_len;
	package.checksum = calc_checksum(package.data, package.valid_len);
	
	return package.valid_len + PACKAGE_INFO == write(socket, &package, package.valid_len + PACKAGE_INFO) ? 0 : -1;
}

static int file_block_send_complete(int socket)
{
	return file_block_send_command(socket, 3, 1);
}

static int file_block_send_block_need(int socket, uint32_t id)
{
	return file_block_send_command(socket, 2, id);
}

static int file_block_parse(int socket, struct file_block *file, uint32_t len)
{
	int ret = 0;

	switch(file->block_type)
	{
		case 0:
			if(0 == sinfo[socket].need_block_total && 0 < file->block_total)
			{
				sinfo[socket].need_block_id = 0;
				sinfo[socket].need_block_total = file->block_total;
				memcpy(sinfo[socket].name, file->data, file->valid_len);
				sinfo[socket].pf = fopen(sinfo[socket].name, "w+");
				if(NULL != sinfo[socket].pf)
				{
					ret = file_block_send_block_need(socket, sinfo[socket].need_block_id);
				}
				else
				{
					ret = -1;
				}
			}
			else
			{
				printf("file name failed\n");
				ret = -1;
			}
			break;
		case 1:
			if(0 == sinfo[socket].need_block_total)
			{
				printf("need_block_total failed\n");
				ret = -1;
			}
			else
			{
				fwrite(file->data, file->valid_len, 1, sinfo[socket].pf);
				sinfo[socket].need_block_id++;
				if(sinfo[socket].need_block_id == sinfo[socket].need_block_total)
				{
					fflush(sinfo[socket].pf);
					fclose(sinfo[socket].pf);
					sinfo[socket].pf = NULL;
					ret = file_block_send_complete(socket);
				}
				else
				{
					ret = file_block_send_block_need(socket, sinfo[socket].need_block_id);
				}
			}
			break;
		default:
			break;
	}
	return ret;
}

static int net_package_checksum(struct net_package *package)
{
	return package->checksum == calc_checksum(package->data, package->valid_len) ? 0 : -1;
}

static int net_package_parse(int socket, struct net_package *package)
{
	int ret = 0;

	if(0 != net_package_checksum(package))
	{
		printf("%s: checksum failed\n", __func__);
		return -1;
	}

	switch(package->type)
	{
		case 0:
			ret = file_block_parse(socket, (struct file_block *)package->data, package->valid_len);
			break;
		case 1:
			break;
		default:
			break;
	}
	return ret;
}

void *thread_recv(void *arg)
{
	int ret = 0;

	int socket = *(int *)arg;

	struct net_package package;

	printf("socket: %d new\n", socket);
	
	memset(&sinfo[socket], 0, sizeof(sinfo[socket]));

	while(1)
	{
		memset(&package, 0, PACKAGE_TOTAL);

		ret = read(socket, &package, PACKAGE_TOTAL);

		if(0 >= ret)
		{
			printf("%s: read failed!!!\n", __func__);
			ret = -1;
			goto loop_free;
		}
		else
		{
			ret = net_package_parse(socket, &package);
			if(0 != ret)
			{
				printf("close\n");
				goto loop_free;
			}
		}
	}

loop_free:
	printf("socket: %d free\n", socket);
	close(socket);
	socket = -1;
	
	*(int *)arg = -1;
	free(arg);

	return (void *)0;
}

int main(int argc, char **argv)
{
	int listen_socket = 0, *client_socket = NULL;
	int opt = 1;

	struct sockaddr_in listen_addr, client_addr;
	socklen_t length = sizeof(client_addr);

	pthread_t tid = (pthread_t)0;
	
	if(PACKAGE_TOTAL != sizeof(struct net_package))
	{
		return -1;
	}

	listen_socket = socket(PF_INET, SOCK_STREAM, 0);

	memset(&listen_addr, 0, sizeof(listen_addr));

	listen_addr.sin_family = AF_INET;
	listen_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	listen_addr.sin_port = htons(56032);

	setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

	bind(listen_socket, (struct sockaddr *)&listen_addr, sizeof(listen_addr));

	listen(listen_socket, 10);

	while(1)
	{
		client_socket = calloc(1, sizeof(*client_socket));

		*client_socket = accept(listen_socket, (struct sockaddr *)&client_addr, &length);
		
		if(0 > *client_socket)
		{
			free(client_socket);
			client_socket = NULL;
		}
		else if(CACHE_SIZE > *client_socket)
		{
			pthread_create(&tid, NULL, thread_recv, client_socket);
			client_socket = NULL;
		}
		else
		{
			close(*client_socket);
			free(client_socket);
			client_socket = NULL;
		}
	}

	return 0;
}